#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
FastAPI WebDAV处理器

基于FastAPI实现轻量级WebDAV服务器，支持302重定向和基础文件操作
"""

import os
import mimetypes
import secrets
from typing import Optional, Dict, Any, List
from datetime import datetime

from fastapi import FastAPI, Request, Response, HTTPException, status, Depends, Header
from fastapi.responses import RedirectResponse, StreamingResponse
from fastapi.security import HTTPBasic, HTTPBasicCredentials
from fastapi.middleware.cors import CORSMiddleware

from ...vfs import VFSClient, VirtualFileSystem
from ...models.user import WebDAVUser
from ...common.database import init_global_session
from ...common.unified_logging import get_logger
from .webdav_xml_builder import xml_builder


class FastAPIWebDAVHandler:
    """FastAPI WebDAV处理器"""
    
    def __init__(self, vfs_client: VFSClient):
        """
        初始化WebDAV处理器
        
        Args:
            vfs_client: VFS客户端实例，必须提供
        """
        self.logger = get_logger('FastAPIWebDAVHandler')
        
        if vfs_client is None:
            raise ValueError("VFSClient is required")
        
        self.vfs_client = vfs_client
        self.logger.info("FastAPI WebDAV处理器初始化完成")


def create_fastapi_webdav_app(vfs_client: VFSClient, webdav_path: str = "dav") -> FastAPI:
    """创建FastAPI WebDAV应用"""
    
    if vfs_client is None:
        raise ValueError("VFSClient is required")
    
    app = FastAPI(
        title="WebDAV Server",
        description="基于FastAPI的轻量级WebDAV服务器",
        version="1.0.0"
    )
    
    # 添加CORS中间件
    app.add_middleware(
        CORSMiddleware,
        allow_origins=["*"],
        allow_credentials=True,
        allow_methods=["*"],
        allow_headers=["*"],
    )
    
    # 初始化处理器
    handler = FastAPIWebDAVHandler(vfs_client)
    
    # HTTP Basic认证
    security = HTTPBasic()
    
    def verify_credentials(credentials: HTTPBasicCredentials = Depends(security)):
        """验证HTTP Basic认证"""
        # 简单的硬编码认证，实际应用中应该从数据库查询
        correct_username = secrets.compare_digest(credentials.username, "testuser")
        correct_password = secrets.compare_digest(credentials.password, "testpass123")
        if not (correct_username and correct_password):
            raise HTTPException(
                status_code=status.HTTP_401_UNAUTHORIZED,
                detail="Incorrect username or password",
                headers={"WWW-Authenticate": "Basic"},
            )
        return credentials.username
    
    # WebDAV根路径
    @app.get("/")
    async def root():
        """根路径"""
        return {"message": "WebDAV Server", "version": "1.0.0"}
    
    # OPTIONS方法 - 返回支持的方法
    @app.options("/")
    async def webdav_options_root():
        """OPTIONS请求 - 根路径"""
        return Response(
            status_code=200,
            headers={
                "Allow": "OPTIONS, HEAD, GET, PUT, DELETE, MKCOL, PROPFIND, PROPPATCH, COPY, MOVE, LOCK, UNLOCK",
                "DAV": "1, 2",
                "MS-Author-Via": "DAV",
                "WWW-Authenticate": "Basic realm=\"WebDAV\""
            }
        )
    
    @app.options("/{path:path}")
    async def webdav_options(path: str):
        """OPTIONS请求 - 带路径"""
        return Response(
            status_code=200,
            headers={
                "Allow": "OPTIONS, HEAD, GET, PUT, DELETE, MKCOL, PROPFIND, PROPPATCH, COPY, MOVE, LOCK, UNLOCK",
                "DAV": "1, 2",
                "MS-Author-Via": "DAV",
                "WWW-Authenticate": "Basic realm=\"WebDAV\""
            }
        )
    
    # GET方法 - 文件下载（302重定向）
    @app.get("/{path:path}")
    async def webdav_get(path: str, username: str = Depends(verify_credentials)):
        """文件下载 - 302重定向到VFS提供的下载URL"""
        try:
            vfs_path = f"/{path}"
            
            # 检查文件是否存在
            if not handler.vfs_client.exists(vfs_path):
                raise HTTPException(status_code=404, detail="File not found")
            
            # 检查是否为目录
            if handler.vfs_client.is_dir(vfs_path):
                raise HTTPException(status_code=405, detail="Method not allowed for directory")
            
            # 获取下载URL
            try:
                download_url = handler.vfs_client.get_download_url(vfs_path)
                handler.logger.info(f"文件下载重定向: {vfs_path} -> {download_url[:100]}...")
                
                # 302重定向
                return RedirectResponse(url=download_url, status_code=302)
            except FileNotFoundError as e:
                # 文件在云存储中不存在，返回404
                handler.logger.warning(f"文件在云存储中不存在: {path} - {e}")
                raise HTTPException(status_code=404, detail="File not found in cloud storage")
            
        except HTTPException:
            raise
        except Exception as e:
            handler.logger.error(f"文件下载失败: {path} - {e}")
            raise HTTPException(status_code=500, detail="Internal server error")
    
    # PUT方法 - 文件上传
    @app.put("/{path:path}")
    async def webdav_put(path: str, request: Request, username: str = Depends(verify_credentials)):
        """文件上传"""
        try:
            vfs_path = f"/{path}"
            
            # 获取文件数据
            file_data = await request.body()
            
            if not file_data:
                raise HTTPException(status_code=400, detail="Empty file data")
            
            # 上传文件
            result = handler.vfs_client.upload_file(file_data, vfs_path, overwrite=True)
            
            if result == 0:
                handler.logger.info(f"文件上传成功: {vfs_path}, 大小: {len(file_data)} bytes")
                return Response(status_code=201)
            else:
                handler.logger.error(f"文件上传失败: {vfs_path}, 返回值: {result}")
                raise HTTPException(status_code=500, detail="Upload failed")
                
        except HTTPException:
            raise
        except Exception as e:
            handler.logger.error(f"文件上传失败: {path} - {e}")
            raise HTTPException(status_code=500, detail="Internal server error")
    
    # DELETE方法 - 删除文件/目录
    @app.delete("/{path:path}")
    async def webdav_delete(path: str, username: str = Depends(verify_credentials)):
        """删除文件或目录"""
        try:
            vfs_path = f"/{path}"
            
            # 检查是否存在
            if not handler.vfs_client.exists(vfs_path):
                # 幂等性：不存在的文件返回成功
                return Response(status_code=204)
            
            # 删除
            success = handler.vfs_client.rm(vfs_path)
            
            if success:
                handler.logger.info(f"删除成功: {vfs_path}")
                return Response(status_code=204)
            else:
                handler.logger.error(f"删除失败: {vfs_path}")
                raise HTTPException(status_code=500, detail="Delete failed")
                
        except HTTPException:
            raise
        except Exception as e:
            handler.logger.error(f"删除失败: {path} - {e}")
            raise HTTPException(status_code=500, detail="Internal server error")
    
    # MKCOL方法 - 创建目录
    @app.api_route("/{path:path}", methods=["MKCOL"])
    async def webdav_mkcol(path: str, request: Request, username: str = Depends(verify_credentials)):
        """创建目录"""
        try:
            # 如果路径为空，说明是根路径，不允许创建根目录
            if not path or path == "":
                raise HTTPException(status_code=405, detail="Cannot create root directory")
            
            vfs_path = f"/{path}"
            
            # 检查是否已存在
            if handler.vfs_client.exists(vfs_path):
                # 根据WebDAV标准，如果目录已存在，返回204 No Content
                handler.logger.info(f"目录已存在: {vfs_path}")
                return Response(status_code=204)
            
            # 创建目录
            result = handler.vfs_client.mkdir(vfs_path)
            
            if result == 0:
                handler.logger.info(f"目录创建成功: {vfs_path}")
                return Response(status_code=201)
            else:
                handler.logger.error(f"目录创建失败: {vfs_path}")
                raise HTTPException(status_code=500, detail="Directory creation failed")
                
        except HTTPException:
            raise
        except Exception as e:
            handler.logger.error(f"目录创建失败: {path} - {e}")
            raise HTTPException(status_code=500, detail="Internal server error")
    
    # PROPFIND方法 - 列出文件/目录属性
    @app.api_route("/", methods=["PROPFIND"])
    async def webdav_propfind_root(request: Request, username: str = Depends(verify_credentials)):
        """PROPFIND请求 - 根路径"""
        try:
            vfs_path = "/"
            
            # 获取Depth头
            depth_header = request.headers.get("Depth", "1")
            depth = 1 if depth_header == "1" else (0 if depth_header == "0" else 1)
            
            files = []
            
            # 根目录：列出子项
            if depth > 0:
                children = handler.vfs_client.ls(vfs_path)
                for child in children:
                    # 确保所有datetime字段都被正确序列化
                    safe_child = {}
                    for key, value in child.items():
                        if isinstance(value, datetime):
                            safe_child[key] = value.isoformat() + 'Z'
                        else:
                            safe_child[key] = value
                    
                    is_dir = safe_child.get('is_dir', False)
                    files.append({
                        'name': safe_child.get('name', ''),
                        'is_directory': is_dir,  # 修复：使用is_dir字段
                        'size': safe_child.get('size', 0),
                        'created_at': safe_child.get('created_at', datetime.utcnow().isoformat() + 'Z'),
                        'modified_at': safe_child.get('updated_at', datetime.utcnow()),  # 修复：使用updated_at字段
                        'content_type': 'httpd/unix-directory' if is_dir else (mimetypes.guess_type(safe_child.get('name', ''))[0] or 'application/octet-stream'),
                        'etag': safe_child.get('etag', 'unknown')
                    })
            
            # 添加根目录本身
            dir_info = {
                'name': '',
                'is_directory': True,
                'size': 0,
                'created_at': datetime.utcnow().isoformat() + 'Z',
                'modified_at': datetime.utcnow(),
                'content_type': 'httpd/unix-directory',
                'etag': 'dir'
            }
            files.insert(0, dir_info)
            
            # 构建XML响应
            base_url = "/dav"
            xml_response = xml_builder.build_propfind_response(files, base_url, depth)
            
            handler.logger.info(f"PROPFIND响应: {vfs_path}, 文件数: {len(files)}")
            
            return Response(
                content=xml_response,
                media_type="application/xml",
                status_code=207,
                headers={
                    "Cache-Control": "no-cache",
                    "Pragma": "no-cache"
                }
            )
            
        except HTTPException:
            raise
        except Exception as e:
            handler.logger.error(f"PROPFIND失败: / - {e}")
            raise HTTPException(status_code=500, detail="Internal server error")

    @app.api_route("/{path:path}", methods=["PROPFIND"])
    async def webdav_propfind(path: str, request: Request, username: str = Depends(verify_credentials)):
        """PROPFIND请求 - 列出文件/目录属性"""
        try:
            # 如果路径为空，说明是根路径，直接返回根路径的响应
            if not path or path == "":
                return await webdav_propfind_root(request, username)
            
            vfs_path = f"/{path}"
            
            # 获取Depth头
            depth_header = request.headers.get("Depth", "1")
            if depth_header == "infinity":
                depth = -1  # -1表示无限深度
            elif depth_header == "0":
                depth = 0
            else:
                depth = 1
            
            # 检查资源是否存在
            if not handler.vfs_client.exists(vfs_path):
                # 返回404响应
                xml_response = xml_builder.build_empty_propfind_response(f"/dav/{path}")
                return Response(
                    content=xml_response,
                    media_type="application/xml",
                    status_code=207
                )
            
            files = []
            
            def get_all_items(current_path: str, current_depth: int):
                """递归获取所有子项"""
                if current_depth == 0:
                    return
                
                if handler.vfs_client.is_dir(current_path):
                    children = handler.vfs_client.ls(current_path)
                    for child in children:
                        # 确保所有datetime字段都被正确序列化
                        safe_child = {}
                        for key, value in child.items():
                            if isinstance(value, datetime):
                                safe_child[key] = value.isoformat() + 'Z'
                            else:
                                safe_child[key] = value
                        
                        is_dir = safe_child.get('is_dir', False)
                        child_name = safe_child.get('name', '')
                        child_path = f"{current_path}/{child_name}" if current_path != "/" else f"/{child_name}"
                        
                        child_info = {
                            'name': child_name,
                            'path': child_path,
                            'is_directory': is_dir,
                            'size': safe_child.get('size', 0),
                            'created_at': safe_child.get('created_at', datetime.utcnow().isoformat() + 'Z'),
                            'modified_at': safe_child.get('updated_at', datetime.utcnow()),
                            'content_type': 'httpd/unix-directory' if is_dir else (safe_child.get('content_type', 'application/octet-stream')),
                            'etag': safe_child.get('etag', 'unknown')
                        }
                        files.append(child_info)
                        
                        # 如果是目录且深度允许，递归获取子项
                        if is_dir and (current_depth == -1 or current_depth > 1):
                            get_all_items(child_path, current_depth - 1 if current_depth > 0 else -1)
            
            if handler.vfs_client.is_dir(vfs_path):
                # 添加目录本身
                dir_info = {
                    'name': os.path.basename(vfs_path) or '/',
                    'path': vfs_path,
                    'is_directory': True,
                    'size': 0,
                    'created_at': datetime.utcnow().isoformat() + 'Z',
                    'modified_at': datetime.utcnow(),
                    'content_type': 'httpd/unix-directory',
                    'etag': 'dir'
                }
                files.append(dir_info)
                
                # 获取子项
                if depth > 0 or depth == -1:
                    get_all_items(vfs_path, depth)
            else:
                # 文件：返回文件信息
                file_info = handler.vfs_client.stat(vfs_path)
                if file_info:
                    # 确保所有datetime字段都被正确序列化
                    safe_file_info = {}
                    for key, value in file_info.items():
                        if isinstance(value, datetime):
                            safe_file_info[key] = value.isoformat() + 'Z'
                        else:
                            safe_file_info[key] = value
                    
                    files.append({
                        'name': os.path.basename(vfs_path),
                        'is_directory': False,
                        'size': safe_file_info.get('size', 0),
                        'created_at': safe_file_info.get('created_at', datetime.utcnow().isoformat() + 'Z'),
                        'modified_at': safe_file_info.get('modified_at', datetime.utcnow()),
                        'content_type': mimetypes.guess_type(vfs_path)[0] or 'application/octet-stream',
                        'etag': safe_file_info.get('etag', 'unknown')
                    })
            
            # 构建XML响应
            base_url = f"/dav/{path}"
            xml_response = xml_builder.build_propfind_response(files, base_url, depth)
            
            handler.logger.info(f"PROPFIND响应: {vfs_path}, 文件数: {len(files)}")
            
            return Response(
                content=xml_response,
                media_type="application/xml",
                status_code=207,
                headers={
                    "Cache-Control": "no-cache",
                    "Pragma": "no-cache"
                }
            )
            
        except HTTPException:
            raise
        except Exception as e:
            handler.logger.error(f"PROPFIND失败: {path} - {e}")
            raise HTTPException(status_code=500, detail="Internal server error")
    
    # PROPPATCH方法 - 设置/删除资源属性
    @app.api_route("/{path:path}", methods=["PROPPATCH"])
    async def webdav_proppatch(path: str, request: Request, username: str = Depends(verify_credentials)):
        """PROPPATCH请求 - 设置/删除资源属性"""
        try:
            # 如果路径为空，说明是根路径
            if not path or path == "":
                vfs_path = "/"
            else:
                vfs_path = f"/{path}"
            
            # 检查资源是否存在
            if not handler.vfs_client.exists(vfs_path):
                raise HTTPException(status_code=404, detail="Resource not found")
            
            # 读取请求体
            body = await request.body()
            
            # 解析XML请求
            try:
                root = ET.fromstring(body)
                handler.logger.info(f"PROPPATCH请求: {vfs_path}")
                
                # 构建响应XML
                multistatus = ET.Element(f"{xml_builder.dav_prefix}multistatus")
                response = ET.SubElement(multistatus, f"{xml_builder.dav_prefix}response")
                
                # href元素
                href = ET.SubElement(response, f"{xml_builder.dav_prefix}href")
                href.text = f"/dav/{path}"
                
                # propstat元素
                propstat = ET.SubElement(response, f"{xml_builder.dav_prefix}propstat")
                prop = ET.SubElement(propstat, f"{xml_builder.dav_prefix}prop")
                
                # status元素
                status = ET.SubElement(propstat, f"{xml_builder.dav_prefix}status")
                status.text = "HTTP/1.1 200 OK"
                
                xml_response = ET.tostring(multistatus, encoding='utf-8', xml_declaration=True)
                
                return Response(
                    content=xml_response,
                    media_type="application/xml",
                    status_code=207
                )
                
            except ET.ParseError as e:
                handler.logger.error(f"PROPPATCH XML解析失败: {e}")
                raise HTTPException(status_code=400, detail="Invalid XML")
                
        except HTTPException:
            raise
        except Exception as e:
            handler.logger.error(f"PROPPATCH失败: {path} - {e}")
            raise HTTPException(status_code=500, detail="Internal server error")
    
    # MOVE方法 - 移动/重命名文件
    @app.api_route("/{path:path}", methods=["MOVE"])
    async def webdav_move(path: str, request: Request, username: str = Depends(verify_credentials)):
        """移动/重命名文件或目录 - 支持覆盖操作"""
        try:
            # 如果路径为空，说明是根路径，不允许移动根目录
            if not path or path == "":
                raise HTTPException(status_code=405, detail="Cannot move root directory")
            
            vfs_path = f"/{path}"
            
            # 获取目标路径
            destination = request.headers.get("Destination")
            if not destination:
                raise HTTPException(status_code=400, detail="Missing Destination header")
            
            # 解析目标路径
            # 支持多种URL格式：http://host:port/dav/path 或 /dav/path
            if destination.startswith("http://"):
                # 提取URL中的路径部分
                from urllib.parse import urlparse, unquote
                parsed_url = urlparse(destination)
                dest_path = unquote(parsed_url.path)  # URL解码
            else:
                from urllib.parse import unquote
                dest_path = unquote(destination)  # URL解码
            
            # 确保路径以 / 开头
            if not dest_path.startswith("/"):
                dest_path = "/" + dest_path
            
            # 移除WebDAV路径前缀，转换为VFS路径
            webdav_prefix = f"/{webdav_path}"
            if dest_path.startswith(f"{webdav_prefix}/"):
                dest_path = dest_path[len(webdav_prefix):]  # 移除WebDAV前缀
            elif dest_path == webdav_prefix:
                dest_path = "/"
            
            # 确保VFS路径以 / 开头
            if not dest_path.startswith("/"):
                dest_path = "/" + dest_path
            
            # 检查源是否存在
            if not handler.vfs_client.exists(vfs_path):
                raise HTTPException(status_code=404, detail="Source not found")
            
            # 检查目标是否已存在，如果存在则先删除（覆盖操作）
            if handler.vfs_client.exists(dest_path):
                handler.logger.info(f"目标文件已存在，执行覆盖操作: {dest_path}")
                try:
                    # 使用强制删除，确保目标文件被完全删除
                    delete_result = handler.vfs_client.rm_force(dest_path)
                    handler.logger.info(f"删除目标文件结果: {delete_result}")
                    
                    # 等待一下确保删除完成
                    import time
                    time.sleep(0.1)
                    
                except Exception as e:
                    handler.logger.warning(f"删除目标文件失败: {dest_path} - {e}")
                    # 即使删除失败，也尝试继续移动操作
            
            # 执行移动操作
            try:
                success = handler.vfs_client.mv(vfs_path, dest_path, overwrite=True)
                if success:
                    handler.logger.info(f"移动成功: {vfs_path} -> {dest_path}")
                    return Response(status_code=201)
                else:
                    handler.logger.error(f"移动失败: {vfs_path} -> {dest_path}")
                    raise HTTPException(status_code=500, detail="Move failed")
            except FileExistsError as e:
                # 如果仍然存在FileExistsError，说明删除没有成功，再次尝试删除
                handler.logger.warning(f"移动时目标文件仍存在，再次尝试删除: {dest_path}")
                try:
                    # 尝试删除目标文件，如果文件不存在则忽略错误
                    try:
                        handler.vfs_client.rm_force(dest_path)
                        handler.logger.info(f"重试删除目标文件成功: {dest_path}")
                    except Exception as delete_e:
                        handler.logger.warning(f"重试删除目标文件失败（可能已不存在）: {dest_path} - {delete_e}")
                    
                    import time
                    time.sleep(0.1)
                    success = handler.vfs_client.mv(vfs_path, dest_path, overwrite=True)
                    if success:
                        handler.logger.info(f"移动成功（重试）: {vfs_path} -> {dest_path}")
                        return Response(status_code=201)
                    else:
                        handler.logger.error(f"移动失败（重试）: {vfs_path} -> {dest_path}")
                        raise HTTPException(status_code=500, detail="Move failed after retry")
                except Exception as retry_e:
                    handler.logger.error(f"重试移动失败: {retry_e}")
                    raise HTTPException(status_code=500, detail="Move failed after retry")
                
        except HTTPException:
            raise
        except Exception as e:
            handler.logger.error(f"移动失败: {path} - {e}")
            raise HTTPException(status_code=500, detail="Internal server error")
    
    # COPY方法 - 复制文件
    @app.api_route("/{path:path}", methods=["COPY"])
    async def webdav_copy(path: str, request: Request, username: str = Depends(verify_credentials)):
        """复制文件或目录 - 支持覆盖操作"""
        try:
            # 如果路径为空，说明是根路径，不允许复制根目录
            if not path or path == "":
                raise HTTPException(status_code=405, detail="Cannot copy root directory")
            
            vfs_path = f"/{path}"
            
            # 获取目标路径
            destination = request.headers.get("Destination")
            if not destination:
                raise HTTPException(status_code=400, detail="Missing Destination header")
            
            # 解析目标路径
            # 支持多种URL格式：http://host:port/dav/path 或 /dav/path
            if destination.startswith("http://"):
                # 提取URL中的路径部分
                from urllib.parse import urlparse, unquote
                parsed_url = urlparse(destination)
                dest_path = unquote(parsed_url.path)  # URL解码
            else:
                from urllib.parse import unquote
                dest_path = unquote(destination)  # URL解码
            
            # 确保路径以 / 开头
            if not dest_path.startswith("/"):
                dest_path = "/" + dest_path
            
            # 移除WebDAV路径前缀，转换为VFS路径
            webdav_prefix = f"/{webdav_path}"
            if dest_path.startswith(f"{webdav_prefix}/"):
                dest_path = dest_path[len(webdav_prefix):]  # 移除WebDAV前缀
            elif dest_path == webdav_prefix:
                dest_path = "/"
            
            # 确保VFS路径以 / 开头
            if not dest_path.startswith("/"):
                dest_path = "/" + dest_path
            
            # 检查源是否存在
            if not handler.vfs_client.exists(vfs_path):
                raise HTTPException(status_code=404, detail="Source not found")
            
            # 检查目标是否已存在，如果存在则先删除（覆盖操作）
            if handler.vfs_client.exists(dest_path):
                handler.logger.info(f"目标文件已存在，执行覆盖操作: {dest_path}")
                try:
                    # 使用强制删除，确保目标文件被完全删除
                    delete_result = handler.vfs_client.rm_force(dest_path)
                    handler.logger.info(f"删除目标文件结果: {delete_result}")
                    
                    # 等待一下确保删除完成
                    import time
                    time.sleep(0.1)
                    
                except Exception as e:
                    handler.logger.warning(f"删除目标文件失败: {dest_path} - {e}")
                    # 即使删除失败，也尝试继续复制操作
            
            # 执行复制操作
            try:
                result = handler.vfs_client.cp(vfs_path, dest_path, overwrite=True)
                if result == 0:
                    handler.logger.info(f"复制成功: {vfs_path} -> {dest_path}")
                    return Response(status_code=201)
                else:
                    handler.logger.error(f"复制失败: {vfs_path} -> {dest_path}")
                    raise HTTPException(status_code=500, detail="Copy failed")
            except FileExistsError as e:
                # 如果仍然存在FileExistsError，说明删除没有成功，再次尝试删除
                handler.logger.warning(f"复制时目标文件仍存在，再次尝试删除: {dest_path}")
                try:
                    # 尝试删除目标文件，如果文件不存在则忽略错误
                    try:
                        handler.vfs_client.rm_force(dest_path)
                        handler.logger.info(f"重试删除目标文件成功: {dest_path}")
                    except Exception as delete_e:
                        handler.logger.warning(f"重试删除目标文件失败（可能已不存在）: {dest_path} - {delete_e}")
                    
                    import time
                    time.sleep(0.1)
                    result = handler.vfs_client.cp(vfs_path, dest_path, overwrite=True)
                    if result == 0:
                        handler.logger.info(f"复制成功（重试）: {vfs_path} -> {dest_path}")
                        return Response(status_code=201)
                    else:
                        handler.logger.error(f"复制失败（重试）: {vfs_path} -> {dest_path}")
                        raise HTTPException(status_code=500, detail="Copy failed after retry")
                except Exception as retry_e:
                    handler.logger.error(f"重试复制失败: {retry_e}")
                    raise HTTPException(status_code=500, detail="Copy failed after retry")
                
        except HTTPException:
            raise
        except Exception as e:
            handler.logger.error(f"复制失败: {path} - {e}")
            raise HTTPException(status_code=500, detail="Internal server error")
    
    # LOCK方法 - 锁定资源
    @app.api_route("/{path:path}", methods=["LOCK"])
    async def webdav_lock(path: str, request: Request, username: str = Depends(verify_credentials)):
        """LOCK请求 - 锁定资源"""
        try:
            # 如果路径为空，说明是根路径
            if not path or path == "":
                vfs_path = "/"
            else:
                vfs_path = f"/{path}"
            
            # 检查资源是否存在
            if not handler.vfs_client.exists(vfs_path):
                raise HTTPException(status_code=404, detail="Resource not found")
            
            # 读取请求体
            body = await request.body()
            
            # 生成锁定令牌
            import uuid
            lock_token = str(uuid.uuid4())
            
            # 构建响应XML
            multistatus = ET.Element(f"{xml_builder.dav_prefix}multistatus")
            response = ET.SubElement(multistatus, f"{xml_builder.dav_prefix}response")
            
            # href元素
            href = ET.SubElement(response, f"{xml_builder.dav_prefix}href")
            href.text = f"/dav/{path}"
            
            # propstat元素
            propstat = ET.SubElement(response, f"{xml_builder.dav_prefix}propstat")
            prop = ET.SubElement(propstat, f"{xml_builder.dav_prefix}prop")
            
            # lockdiscovery元素
            lockdiscovery = ET.SubElement(prop, f"{xml_builder.dav_prefix}lockdiscovery")
            activelock = ET.SubElement(lockdiscovery, f"{xml_builder.dav_prefix}activelock")
            
            # locktype元素
            locktype = ET.SubElement(activelock, f"{xml_builder.dav_prefix}locktype")
            ET.SubElement(locktype, f"{xml_builder.dav_prefix}write")
            
            # lockscope元素
            lockscope = ET.SubElement(activelock, f"{xml_builder.dav_prefix}lockscope")
            ET.SubElement(lockscope, f"{xml_builder.dav_prefix}exclusive")
            
            # depth元素
            depth = ET.SubElement(activelock, f"{xml_builder.dav_prefix}depth")
            depth.text = "infinity"
            
            # owner元素
            owner = ET.SubElement(activelock, f"{xml_builder.dav_prefix}owner")
            owner_href = ET.SubElement(owner, f"{xml_builder.dav_prefix}href")
            owner_href.text = username
            
            # timeout元素
            timeout = ET.SubElement(activelock, f"{xml_builder.dav_prefix}timeout")
            timeout.text = "Second-3600"
            
            # locktoken元素
            locktoken = ET.SubElement(activelock, f"{xml_builder.dav_prefix}locktoken")
            locktoken_href = ET.SubElement(locktoken, f"{xml_builder.dav_prefix}href")
            locktoken_href.text = lock_token
            
            # status元素
            status = ET.SubElement(propstat, f"{xml_builder.dav_prefix}status")
            status.text = "HTTP/1.1 200 OK"
            
            xml_response = ET.tostring(multistatus, encoding='utf-8', xml_declaration=True)
            
            handler.logger.info(f"资源锁定成功: {vfs_path}, 令牌: {lock_token}")
            
            return Response(
                content=xml_response,
                media_type="application/xml",
                status_code=200,
                headers={
                    "Lock-Token": f"<{lock_token}>"
                }
            )
            
        except HTTPException:
            raise
        except Exception as e:
            handler.logger.error(f"资源锁定失败: {path} - {e}")
            raise HTTPException(status_code=500, detail="Internal server error")
    
    # UNLOCK方法 - 解锁资源
    @app.api_route("/{path:path}", methods=["UNLOCK"])
    async def webdav_unlock(path: str, request: Request, username: str = Depends(verify_credentials)):
        """UNLOCK请求 - 解锁资源"""
        try:
            # 如果路径为空，说明是根路径
            if not path or path == "":
                vfs_path = "/"
            else:
                vfs_path = f"/{path}"
            
            # 获取锁定令牌
            lock_token = request.headers.get("Lock-Token", "")
            if not lock_token:
                raise HTTPException(status_code=400, detail="Missing Lock-Token header")
            
            # 检查资源是否存在
            if not handler.vfs_client.exists(vfs_path):
                raise HTTPException(status_code=404, detail="Resource not found")
            
            handler.logger.info(f"资源解锁成功: {vfs_path}, 令牌: {lock_token}")
            
            return Response(status_code=204)
            
        except HTTPException:
            raise
        except Exception as e:
            handler.logger.error(f"资源解锁失败: {path} - {e}")
            raise HTTPException(status_code=500, detail="Internal server error")
    
    return app
